/*
 * Copyright 2006-2008 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.martinimix.dao;

import java.util.Map;

import net.martinimix.beans.bind.BindingService;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.bluemartini.dna.BMException;
import com.bluemartini.dna.DNAList;
import com.bluemartini.dna.DNAListArray;
import com.bluemartini.dna.DNAStringArray;
import com.bluemartini.server.BMClient;
import com.bluemartini.server.BusinessActionDef;
import com.bluemartini.server.BusinessActionDictionary;

/**
 * @author scott
 *
 */
public class BusinessActionChainTemplate implements BusinessActionChain {
	
	private final static Log log = LogFactory.getLog(BusinessActionChainTemplate.class);
	
	/**
	 * The binding service for the business action template.
	 */
	protected BindingService bindingService;
	
	/**
	 * The business action toolkit for this template. 
	 */
	protected BusinessActionToolkit businessActionToolkit;
	
	/**
	 * Constructs a new business action chain template with the given binding service.
	 * 
	 * @param bindingService
	 */
	public BusinessActionChainTemplate(BindingService bindingService) {
		
		if(bindingService == null) {
			throw new IllegalArgumentException("[BindingService] cannot be null!");
		}
		
		this.bindingService = bindingService;
		this.businessActionToolkit = new BusinessActionToolkit(bindingService);
	}

	private void checkArgs(String[] actions, Map input, BusinessActionResponseMapper mapper) {

		if(actions == null || actions.length == 0) {
			throw new IllegalArgumentException("[actions] cannot be null or empty!");
		}
		
		if(input == null) {
			throw new IllegalArgumentException("[input] canot be null!");
		}
		
		if(mapper == null) {
			throw new IllegalArgumentException("[mapper] cannot be null!");
		}
	}
	
	/* (non-Javadoc)
	 * @see net.martinimix.dao.BusinessActionChain#executeChain(java.lang.String[], java.util.Map, net.martinimix.dao.BusinessActionResponseMapper)
	 */
	public Map executeChain(String[] actions, Map input, BusinessActionResponseMapper mapper, boolean wrapFormData) {

		checkArgs(actions, input, mapper);
		BusinessActionChainInput chain = new BusinessActionChainInput(
				businessActionToolkit.createBusinessActionInput(input, wrapFormData));
		
		return mapper.map(doChain(actions, chain));
	}
	
	protected DNAList doChain(String[] actions, BusinessActionChainInput chain) {
		
		DNAList response = null;
		
		for(int i = 0; i < actions.length; i++) {
			
			if(log.isDebugEnabled()) {
				log.debug("Executing chained action [" + actions[i] + "]");
			}
			response = doBusinessAction(actions[i], chain.getRequest());
			if(log.isDebugEnabled()) {
				log.debug("Mergining [" + actions[i] + "] response [" + response + "]");
			}
			chain.update(response);
		}
		
		return chain.getRequest();
	}
	
	/**
	 * Executes the given business action and returns its response.
	 * 
	 * @param action the name of the <code>BusinessAction</code> to execute
	 * 
	 * @param input a <code>DNAList</code> containing the input for the given
	 * <code>action</code>
	 * 
	 * @return
	 */
	protected final DNAList doBusinessAction(String action, DNAList input) {
		
		BusinessActionDef def = BusinessActionDictionary.getDefinition(action);
		
		if(def == null) {
			throw new BusinessActionNotFoundException(
					"Business action [" + action + "] definition could be be found!");
		}
		
		 DNAListArray inputDef = def.getInputs();
		 DNAStringArray path;
		 DNAList dnaIn;
		 
		 if(inputDef == null || inputDef.size() == 0) {
			 throw new BusinessActionDefinitionException(
					 "BusinessAction [" + action + "] inputs must defined in DNA file!");
		 }
		 
		 
		 path = inputDef.elementAt(0).getStringArray("path");
		 if(path.contains("formData")) {
			 dnaIn = new DNAList();
			 dnaIn.setListRef("formData", input);
		 } else {
			 dnaIn = input;
		 }
		 
		 if(log.isDebugEnabled()) {
			 log.debug("Executing [" + action + "] with input [" + dnaIn + "]");
		 }
		 
		try {
			return BMClient.executeBusinessAction(action, dnaIn);
		} catch (BMException e) {
			
			if(log.isDebugEnabled()) {
				log.debug(e);
			}
			
			throw new BusinessActionInvocationException("Error invoking BusinessAction [" + action + "]", e);
		}
	}

}
